package org.e_gold.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.e_gold.dao.CookieDAO;
import org.e_gold.dao.EventDAO;
import org.e_gold.dao.SettledContractDAO;
import org.e_gold.factory.Manager;
import org.e_gold.model.Contract;
import org.e_gold.model.Event;
import org.e_gold.model.SettledContract;
import org.e_gold.service.AIService;
import org.e_gold.service.BrowserService;
import org.e_gold.service.ContractService;
import org.e_gold.service.EventService;
import org.e_gold.service.SettledContractService;
import org.e_gold.service.UserAccountService;
import org.e_gold.util.Bet365;
import org.e_gold.util.CalcUtil;
import org.e_gold.util.Constant;
import org.e_gold.util.LogUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class BrowserServiceImpl extends _NavigatorService implements BrowserService, Constant, Bet365 {

    private static final Logger logger = Logger.getLogger(BrowserServiceImpl.class);
    @Autowired
    private CookieDAO cookieDAO;

    @Autowired
    private EventService eventService;

    @Autowired
    private ContractService contractService;

    @Autowired
    private AIService aiService;

    @Autowired
    private SettledContractDAO settledContractDAO;

    @Autowired
    private SettledContractService settledContractService;

    @Autowired
    private EventDAO eventDAO;

    @Autowired
    private UserAccountService userAccountService;

    @Override
    public List<Event> listAllEvent() {

        return null;
    }

    public void initCookiesAndHeaders() {

        headers = prepareBasicHeaders();
        if (cookieDAO.findAll() != null && cookieDAO.findAll().size() > 0) {
            // re-store cookie for logined session or basic session for the first time
            if (cookies.getCookies() == null) {
                cookies.setCookies(cookieDAO.findAll());
            }
            headers.put(COOKIE, cookies.getCookie());
        } else {
            try {
                redirectNavigator(DEFAULT_URL);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void exposeEvents() {

        try {
            initCookiesAndHeaders();
            // go to In-Play -> Soccer
            Document soccers = redirectNavigator(SOCCER_IN_PLAY_URL);
            Elements eEvents = soccers.select("div.Level2");
            List<Event> events = eventService.exposeAllEvent(eEvents);
            // persist events
            events = eventService.persistEvent(events);
            LogUtil.logs(events);
            List<Contract> allContracts = contractService.exposeAllContract(events, headers, cookies);
            if (allContracts != null && allContracts.size() > 0) {
                List<Contract> qualifyContracts = aiService.getQualifyContract(allContracts); // filter
                if (qualifyContracts != null) {
                    Manager.STATUS = 1; // place bet - waiting for finish
                    aiService.virtualPlaceBet(qualifyContracts);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void waitingResult() {

        try {
            logger.info("::Waiting result : " + new Date());
            SettledContract running = settledContractDAO.getRunningSettledContract();
            if (running != null) {
                Event runningEvent = settledContractService.getRunningEvent(running);
                Document runningPage = null;
                try {
                    runningPage = redirectNavigator(runningEvent.getLocation());

                } catch (Exception e) {
                    logger.info(" waitingResult -> redirectNavigator OR persist event error ");
                }
                if (runningPage.select(".Score").size() > 0 && runningPage.select(".Score") != null) {
                    Element eScore = runningPage.select(".Score").get(0);
                    // update running settled contract
                    running.setCurrentGoal(CalcUtil.getTotalGoal(eScore.text()));
                    runningEvent.setPlayTime(CalcUtil.getCurrentTime(runningPage.select("#ct0").get(0).text()));
                    runningEvent.setTotalGoal(CalcUtil.getTotalGoal(eScore.text()));
                    eventDAO.update(runningEvent);
                    logger.info("Event play time : " + runningEvent.getPlayTime());
                    logger.info("Event current goals : " + runningEvent.getTotalGoal());
                    settledContractDAO.update(running);
                } else {
                    Element eFinish = null;
                    if (runningPage.select(".Level1") == null && runningEvent.getPlayTime() > 90) { // match done, check money and continue bet
                        finishedBet(running, runningEvent);
                    }
                    try {
                        eFinish = runningPage.select(".Level1").get(0);
                        if (eFinish != null) {
                            if (eFinish.text().indexOf("this page is no longer available") > -1 && runningEvent.getPlayTime() >= 85) {
                                finishedBet(running, runningEvent);
                            }
                        }
                    } catch (Exception e) {
                        logger.info("Match done error while parse");
                    }
                }
            } else {
                Manager.STATUS = 0;
            }
        } catch (Exception e) {
            logger.info("waitingResult error");
            e.printStackTrace();
        }
    }

    @Override
    public boolean placeBet(Contract bestContract) {

        try {
            logger.info("Check authentication & login if user has not session");
            userAccountService.login();
            if (bestContract != null) {
                logger.info("Add Bet Slip");
                Document placeBetPage = redirectNavigator(bestContract.getBetSlipUrl());
                // check still valid
                if (placeBetStillValid(placeBetPage)) {
                    logger.info("remove all other bet slips if existed");
                    placeBetPage = removeAllBetSlips(placeBetPage, bestContract.getBetSlipUrl());
                    if (placeBetPage != null) {
                        // logger.info(placeBetPage);
                        Map<String, String> formData = new HashMap<String, String>();
                        formData.put("__VIEWSTATE", "");
                        formData.put("sn1", userAccountService.getRealBalance().toString());
                        formData.put("atbs", "Place Bet");
                        // place bet action
                        logger.info("postForm place bet timeout 30s !!!");
                        Document betReceiptPage = postForm(PLACE_BET_SLIP, formData);
                        // logger.info(betReceiptPage);
                        logger.info("Check settled is successful or not");
                        if (settledSuccessful(betReceiptPage)) {
                            return true;
                        } else {
                            return false;
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return false;
    }

    private Document removeAllBetSlips(Document placeBetPage, String betSlipUrl) {

        Elements betSlip = placeBetPage.select(".BetSlip");
        logger.info("Number of bet slips : " + betSlip.size());
        if (betSlip != null && betSlip.size() > 1) {
            int removeTime = betSlip.size() - 1;
            for (int i = 0; i < removeTime; i++) {
                try {
                    logger.info("Remove first bet slip until left one");
                    placeBetPage = redirectNavigator(REMOVE_FIRST_BET_SLIP);
                } catch (Exception e) {
                    logger.error(e);
                    return null;
                }
            }
        }
        return placeBetPage;
    }

    private boolean settledSuccessful(Document betReceiptPage) {

        if (betReceiptPage.select(".oddsPanel") != null) {
            Element betRef = betReceiptPage.select(".oddsPanel").get(0);
            if (betRef != null && betRef.text().indexOf("To Return") > -1) {
                logger.info("Settled bet successful");
                try {
                    logger.info("Total return : " + betRef.text());
                } catch (Exception e) {
                    logger.error("just log not effect to main flow" + e);
                }
                return true;
            }
        }
        return false;
    }

    private boolean placeBetStillValid(Document placeBetPage) {

        try {
            String betslipHasChanged = placeBetPage.select(".menuRow").text();
            logger.info(betslipHasChanged);
            if (!StringUtils.isEmpty(betslipHasChanged) && betslipHasChanged.indexOf("betslip has changed") > -1) {
                logger.info("placeBetStillValid has not valid any more - return false - change diff contract");
                return false;
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return true;
    }

    private void finishedBet(SettledContract running, Event runningEvent) {

        Manager.STATUS = 0; // re-call expose events in manager thread
        settledContractService.updateAllRunningToFalse();
        Boolean result = aiService.calcResult(running);

        logger.info(":::::::::::::::::::::::::::::::::::::");
        logger.info(":::::::::::::::::::::::::::::::::::::");
        logger.info("Event finised - Result : " + result);
        logger.info("Event play time : " + runningEvent.getPlayTime());
        logger.info("Event current goals : " + runningEvent.getTotalGoal());
        logger.info(":::::::::::::::::::::::::::::::::::::");
        logger.info(":::::::::::::::::::::::::::::::::::::");
    }
}
